package com.ruoyi.framework.config;

import com.ruoyi.common.config.HookNotification;
import com.ruoyi.common.utils.Threads;
import io.netty.util.internal.StringUtil;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.lang.reflect.Field;
import java.util.concurrent.*;

/**
 * 线程池配置
 *
 * @author ruoyi
 **/
@Configuration
@EnableAsync
public class ThreadPoolConfig {
    // webhook url
    @Value("${notification.url}")
    private String notificationUrl;

    // 核心线程池大小
    private final int corePoolSize = 4;

    // 最大可创建的线程数
    private final int maxPoolSize = 20;

    // 队列最大长度
    private final int queueCapacity = 100;

    // 线程池维护线程所允许的空闲时间
    private final int keepAliveSeconds = 300;

    @Bean(name = "threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setMaxPoolSize(maxPoolSize);
        executor.setCorePoolSize(corePoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setKeepAliveSeconds(keepAliveSeconds);
        // 线程池对拒绝任务(无线程可用)的处理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }

    /**
     * 执行周期性或定时任务
     */
    @Bean(name = "scheduledExecutorService")
    protected ScheduledExecutorService scheduledExecutorService() {
        return new ScheduledThreadPoolExecutor(corePoolSize,
                new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
                new ThreadPoolExecutor.CallerRunsPolicy()) {
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                super.afterExecute(r, t);
                Threads.printException(r, t);
            }
        };
    }

    @Bean(name = "qywxCallbackExecutor")
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize); // 线程池大小
        executor.setMaxPoolSize(maxPoolSize); // 最大线程池大小
        executor.setQueueCapacity(queueCapacity); // 任务队列容量
        executor.setKeepAliveSeconds(keepAliveSeconds);
        executor.setThreadNamePrefix("qywxCbExecutor-"); // 线程前缀名
        executor.setRejectedExecutionHandler(callBackExecution("企业微信回调")); // 任务拒绝策略
        executor.initialize();
        return executor;
    }

    @Bean(name = "qywxEventExecutor")
    public ThreadPoolTaskExecutor qywxEventExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize); // 线程池大小
        executor.setMaxPoolSize(maxPoolSize); // 最大线程池大小
        executor.setQueueCapacity(queueCapacity); // 任务队列容量
        executor.setKeepAliveSeconds(keepAliveSeconds);
        executor.setThreadNamePrefix("qywxEvExecutor-"); // 线程前缀名
        executor.setRejectedExecutionHandler(callBackExecution("企业微信事件")); // 任务拒绝策略
        executor.initialize();
        return executor;
    }


    private RejectedExecutionHandler callBackExecution(String cbPrefix) {
        return new CallBackRunsPolicy(cbPrefix);
    }

    private class CallBackRunsPolicy implements RejectedExecutionHandler {
        private final String channel;
        private final HookNotification errNotification;

        private CallBackRunsPolicy(String channel) {
            this.channel = channel;
            this.errNotification = new HookNotification(notificationUrl);
        }

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            try {
                Field field = r.getClass().getDeclaredField("callable");
                field.setAccessible(true);
                Callable callable = (Callable) field.get(r);
                Field invocationField = callable.getClass().getDeclaredField("arg$2");
                invocationField.setAccessible(true);
                MethodInvocation invocation = (MethodInvocation) invocationField.get(callable);
                Object[] args = invocation.getArguments();
                StringBuilder builder = new StringBuilder();
                for (Object arg : args) {
                    builder.append(arg.toString()).append(StringUtil.LINE_FEED);
                }
                this.errNotification.error("线程池: " + this.channel + "已满，未执行任务：{}\n" + builder);
            } catch (Exception err) {
                err.printStackTrace();
            }
        }
    }
}
